package cn.smile.business.basic.impl;

import cn.smile.bean.dto.basic.*;
import cn.smile.bean.dto.core.SysUserDTO;
import cn.smile.bean.entity.basic.ExpenseBill;
import cn.smile.bean.form.basic.bill.ExpBaseItemForm;
import cn.smile.bean.form.basic.bill.ExpenseAddForm;
import cn.smile.bean.form.basic.bill.QueryExpenseForm;
import cn.smile.bean.form.basic.bill.billBasicItemAddForm;
import cn.smile.bean.form.basic.item.QueryBasicItemForm;
import cn.smile.bean.form.basic.member.QueryMemberForm;
import cn.smile.bean.form.basic.member.UpdateBalanceForm;
import cn.smile.bean.form.basic.voucher.QueryVouMemForm;
import cn.smile.bean.form.core.user.QueryUserForm;
import cn.smile.business.basic.*;
import cn.smile.business.core.ISysUserService;
import cn.smile.commons.constant.CommonConstant;
import cn.smile.commons.constant.MemberConstant;
import cn.smile.commons.constant.NumberConstant;
import cn.smile.commons.domain.BaseServiceImpl;
import cn.smile.commons.enums.basic.BalanceOperateEnum;
import cn.smile.commons.enums.basic.MiniProjectCountEnum;
import cn.smile.commons.enums.basic.VoucherStatusEnum;
import cn.smile.commons.exceptions.BusinessException;
import cn.smile.commons.response.MyResponseCode;
import cn.smile.repository.basic.ExpenseBillMapper;
import cn.smile.utils.BeanCopy;
import cn.smile.utils.DateFormatUtil;
import cn.smile.utils.UserUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import jakarta.annotation.Resource;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class ExpenseBillServiceImpl extends BaseServiceImpl<ExpenseBillMapper, ExpenseBill> implements IExpenseBillService {

    @Resource
    private IMemberService memberService;
    @Resource
    private IMyShopService shopService;
    @Resource
    private IExpenseItemService itemService;
    @Resource
    private IBillBasicItemService billItemService;
    @Resource
    private IBasicItemService basicItemService;
    @Resource
    private ISysUserService userService;
    @Resource
    private IVoucherMemberService voucherMemberService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addExpense(ExpenseAddForm form) {
        log.info("[ExpenseBillServiceImpl].[addBill] ------> 新增消费账单 Start, form = {}", JSON.toJSONString(form));
        this.checkVoucher(form.getVouMemId());
        ExpenseBill entity = BeanCopy.copyBean(form, ExpenseBill.class);
        MyShopDTO shop = shopService.getMyShop();
        entity.setShopId(shop.getId());
        super.addBaseCol(entity);
        entity.setBillTime(LocalDateTime.now());
        this.handleDiscount(form, entity);
        entity.setOperateUserId(UserUtil.getUserInfo().getId());
        int insertNum = baseMapper.insert(entity);
        if (insertNum > NumberConstant.ZERO) {
            this.saveBasicItemList(entity.getId(), form.getBaseItemList());
            this.updateVoucherStatus(form.getVouMemId());
        }
        log.info("[ExpenseBillServiceImpl].[addBill] ------> 新增消费账单 End, insertNum = {}", insertNum);
        return insertNum > NumberConstant.ZERO;
    }

    @Override
    public IPage<ExpenseBillDTO> getPage(QueryExpenseForm form) {
        log.info("[ExpenseBillServiceImpl].[getPage] ------> 分页查询消费账单 Start, form = {}", JSON.toJSONString(form));
        LambdaQueryWrapper<ExpenseBill> qw = this.getQueryParams(form);
        Page<ExpenseBill> basePage = baseMapper.selectPage(form.queryPage(), qw);
        IPage<ExpenseBillDTO> dataPage = BeanCopy.copyPage(basePage, ExpenseBillDTO.class);
        this.setDetail(dataPage.getRecords());
        log.info("[ExpenseBillServiceImpl].[getPage] ------> 分页查询消费账单 End");
        return dataPage;
    }

    @Override
    public List<ExpenseBillDTO> getList(QueryExpenseForm form) {
        log.info("[ExpenseBillServiceImpl].[getList] ------> 列表查询消费账单 Start, form = {}", JSON.toJSONString(form));
        LambdaQueryWrapper<ExpenseBill> qw = this.getQueryParams(form);
        List<ExpenseBill> baseList = baseMapper.selectList(qw);
        List<ExpenseBillDTO> dataList = BeanCopy.copyList(baseList, ExpenseBillDTO.class);
        this.setDetail(dataList);
        log.info("[ExpenseBillServiceImpl].[getList] ------> 列表查询消费账单 End");
        return dataList;
    }

    @Override
    public BigDecimal countExpenseAmount(MiniProjectCountEnum countEnum) {
        Date nowDate = new Date();
        String startTime = DateFormatUtil.getCountStartTime(nowDate, countEnum);
        String startEnd = DateFormatUtil.parseDateToStr(nowDate, DateFormatUtil.TO_DATE_END);
        return this.countExpenseAmount(startTime, startEnd, Boolean.FALSE);
    }

    @Override
    public BigDecimal countExpenseAmount(String timeStart, String timeEnd, Boolean isMember) {
        log.info("[ExpenseBillServiceImpl].[countExpenseAmount] ------> 统计店铺消费金额 Start, timeStart = {}, timeEnd = {}, isMember = {}", timeStart, timeEnd, isMember);
        BigDecimal todayAmount = CommonConstant.DEFAULT_PRICE;
        LambdaQueryWrapper<ExpenseBill> qw = this.getQueryParams(timeStart, timeEnd, isMember);
        List<ExpenseBill> baseList = baseMapper.selectList(qw);
        if (!CollectionUtils.isEmpty(baseList)) {
            for (ExpenseBill entity : baseList) {
                todayAmount = todayAmount.add(entity.getFinalPrice());
            }
        }
        log.info("[ExpenseBillServiceImpl].[countExpenseAmount] ------> 统计店铺消费金额 End, todayAmount = {}", todayAmount);
        return todayAmount;
    }

    @Override
    public BigDecimal countGiftExpenseAmount(String timeStart, String timeEnd) {
        log.info("[ExpenseBillServiceImpl].[countGiftExpenseAmount] ------> 统计店铺消费赠送金额 Start, timeStart = {}, timeEnd = {}", timeStart, timeEnd);
        BigDecimal amount = CommonConstant.DEFAULT_PRICE;
        LambdaQueryWrapper<ExpenseBill> qw = this.getQueryParams(timeStart, timeEnd, Boolean.TRUE);
        List<ExpenseBill> baseList = baseMapper.selectList(qw);
        if (!CollectionUtils.isEmpty(baseList)) {
            for (ExpenseBill entity : baseList) {
                if (!ObjectUtils.isEmpty(entity.getGiftPrice())) {
                    amount = amount.add(entity.getGiftPrice());
                }
            }
        }
        log.info("[ExpenseBillServiceImpl].[countGiftExpenseAmount] ------> 统计店铺消费赠送金额 End, amount = {}", amount);
        return amount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delExpense(String id) {
        log.info("[ExpenseBillServiceImpl].[delExpense] ------> 删除消费账单 Start, id = {}", id);
        if (ObjectUtils.isEmpty(id)) {
            throw new BusinessException(MyResponseCode.EXPENSE_ID_NULL_ERROR);
        }
        ExpenseBill entity = baseMapper.selectOne(new QueryWrapper<ExpenseBill>().lambda().eq(ExpenseBill::getDeleted, Boolean.FALSE).eq(ExpenseBill::getId, id));
        if (ObjectUtils.isEmpty(entity)) {
            throw new BusinessException(MyResponseCode.RESULT_DATA_NONE);
        }
        super.delBaseCol(entity);
        int updateNum = baseMapper.updateById(entity);
        if (updateNum > NumberConstant.ZERO && !ObjectUtils.isEmpty(entity.getMemberId())) {
            UpdateBalanceForm balanceForm = new UpdateBalanceForm();
            balanceForm.setMemberId(entity.getMemberId());
            balanceForm.setAmount(entity.getFinalPrice());
            balanceForm.setGiftPrice(entity.getGiftPrice());
            memberService.updateBalance(balanceForm, BalanceOperateEnum.TOP_UP);
            Boolean flag = billItemService.delByBillId(entity.getId());
            log.info("删除消费账单基础项目完成, {}", flag);
            if (!ObjectUtils.isEmpty(entity.getVouMemId())) {
                voucherMemberService.updateVoucherStatus(entity.getVouMemId(), VoucherStatusEnum.UN_USE);
            }
        }
        log.info("[ExpenseBillServiceImpl].[delExpense] ------> 删除消费账单 End, updateNum = {}", updateNum);
        return updateNum > NumberConstant.ZERO;
    }

    @Override
    public Long countOrderNum(String timeStart, String timeEnd, Boolean isMember) {
        log.info("[ExpenseBillServiceImpl].[countOrderNum] ------> 统计完成订单数量 Start, timeStart = {}, timeEnd = {}, isMember = {}", timeStart, timeEnd, isMember);
        LambdaQueryWrapper<ExpenseBill> qw = this.getQueryParams(timeStart, timeEnd, isMember);
        Long orderNum = baseMapper.selectCount(qw);
        log.info("[ExpenseBillServiceImpl].[countOrderNum] ------> 统计完成订单数量 End, orderNum = {}", orderNum);
        return orderNum;
    }

    @Override
    public ExpenseBillDTO getExpenseById(String id) {
        log.info("[ExpenseBillServiceImpl].[getExpenseById] ------> 查询消费账单详情 Start, id = {}", id);
        ExpenseBill baseEntity = baseMapper.selectOne(new QueryWrapper<ExpenseBill>().lambda().eq(ExpenseBill::getDeleted, Boolean.FALSE).eq(ExpenseBill::getId, id));
        if (ObjectUtils.isEmpty(baseEntity)) {
            throw new BusinessException(MyResponseCode.RESULT_DATA_NONE);
        }
        ExpenseBillDTO dto = BeanCopy.copyBean(baseEntity, ExpenseBillDTO.class);
        this.setDetail(Lists.newArrayList(dto));
        log.info("[ExpenseBillServiceImpl].[getExpenseById] ------> 查询消费账单详情 End");
        return dto;
    }

    /**
     * 保存账单基础项目
     *
     * @param billId       账单ID
     * @param baseItemList 基础项目集合
     */
    private void saveBasicItemList(String billId, List<ExpBaseItemForm> baseItemList) {
        if (!CollectionUtils.isEmpty(baseItemList)) {
            Set<String> basicIds = new HashSet<>(NumberConstant.EIGHT);
            baseItemList.forEach(item -> basicIds.add(item.getId()));
            Map<String, BasicItemDTO> basicMap = Maps.newHashMap();
            QueryBasicItemForm queryForm = new QueryBasicItemForm();
            queryForm.setIds(Lists.newArrayList(basicIds));
            List<BasicItemDTO> basicList = basicItemService.getList(queryForm);
            if (!CollectionUtils.isEmpty(basicList)) {
                basicList.forEach(item -> basicMap.put(item.getId(), item));
            }

            for (ExpBaseItemForm baseItemForm : baseItemList) {
                billBasicItemAddForm addForm = new billBasicItemAddForm();
                addForm.setBillId(billId);
                addForm.setBasicId(baseItemForm.getId());
                addForm.setOpeUserId(baseItemForm.getOpeUserId());
                String baseName = basicMap.getOrDefault(baseItemForm.getId(), new BasicItemDTO()).getBaseName();
                addForm.setBasicName(baseName);
                Boolean flag = billItemService.saveBillBasicItem(addForm);
                log.info("账单 [{}] 基础项 [{}] 保存结果: {}", billId, baseName, flag);
            }
        }
    }

    /**
     * 查询参数
     *
     * @param timeStart 查询开始时间
     * @param timeEnd   查询结束时间
     * @param isMember  会员标记(true-会员, false-非会员)
     * @return 数据库查询对象
     */
    private LambdaQueryWrapper<ExpenseBill> getQueryParams(String timeStart, String timeEnd, Boolean isMember) {
        LambdaQueryWrapper<ExpenseBill> qw = new QueryWrapper<ExpenseBill>().lambda().eq(ExpenseBill::getDeleted, Boolean.FALSE);
        MyShopDTO shop = shopService.getMyShop();
        qw.eq(ExpenseBill::getShopId, shop.getId());
        qw.ge(ExpenseBill::getBillTime, timeStart);
        qw.le(ExpenseBill::getBillTime, timeEnd);
        if (!ObjectUtils.isEmpty(isMember)) {
            if (isMember) {
                qw.isNotNull(ExpenseBill::getMemberId);
            } else {
                qw.isNull(ExpenseBill::getMemberId);
            }
        }
        return qw;
    }

    /**
     * 写入消费账单其他信息
     *
     * @param dataList 消费账单集合
     */
    @SuppressWarnings("DuplicatedCode")
    private void setDetail(List<ExpenseBillDTO> dataList) {
        if (!CollectionUtils.isEmpty(dataList)) {
            Set<String> memberIds = Sets.newHashSet();
            Set<String> userIds = Sets.newHashSet();
            List<String> ids = Lists.newArrayList();
            Set<String> vouMemIds = Sets.newHashSet();
            dataList.forEach(dto -> {
                if (!ObjectUtils.isEmpty(dto.getMemberId())) {
                    memberIds.add(dto.getMemberId());
                    ids.add(dto.getId());
                    if (!ObjectUtils.isEmpty(dto.getOperateUserId())) {
                        userIds.add(dto.getOperateUserId());
                    }
                }
                if (!ObjectUtils.isEmpty(dto.getVouMemId())) {
                    vouMemIds.add(dto.getVouMemId());
                }
            });
            //查询会员代金券信息集合
            Map<String, VoucherMemberDTO> vouMemMap = this.getVouMemMap(Lists.newArrayList(vouMemIds));
            //查询会员信息
            Map<String, MemberInfoDTO> memberMap = Maps.newHashMap();
            if (!CollectionUtils.isEmpty(memberIds)) {
                QueryMemberForm memberForm = new QueryMemberForm();
                memberForm.setIds(Lists.newArrayList(memberIds));
                List<MemberInfoDTO> memberList = memberService.getMemberList(memberForm);
                if (!CollectionUtils.isEmpty(memberList)) {
                    for (MemberInfoDTO dto : memberList) {
                        memberMap.put(dto.getId(), dto);
                    }
                }
            }
            //查询基础项目集合
            Map<String, List<BillBasicItemDTO>> basicMap = billItemService.getBasicByBillIds(ids);
            //查询用户集合
            QueryUserForm form = new QueryUserForm();
            form.setIds(Lists.newArrayList(userIds));
            Map<String, SysUserDTO> userMap = userService.getMyShopUserMap(form);
            //查询消费项目信息
            MyShopDTO shop = shopService.getMyShop();
            Map<String, ExpenseItemDTO> itemMap = itemService.getItemMap(shop.getId());
            //写入会员信息
            for (ExpenseBillDTO dto : dataList) {
                if (!ObjectUtils.isEmpty(dto.getVouMemId())) {
                    dto.setVoucherName(vouMemMap.getOrDefault(dto.getVouMemId(), new VoucherMemberDTO()).getVoucherName());
                }
                dto.setSimplifyBillTime(dto.getBillTime());
                if (basicMap.containsKey(dto.getId())) {
                    dto.setBasicItemList(basicMap.get(dto.getId()));
                }
                if (!ObjectUtils.isEmpty(dto.getOperateUserId()) && userMap.containsKey(dto.getOperateUserId())) {
                    dto.setOperateUserName(userMap.get(dto.getOperateUserId()).getNickName());
                    dto.setOperateUser(userMap.get(dto.getOperateUserId()));
                }
                ExpenseItemDTO item = itemMap.getOrDefault(dto.getExpenseItemId(), null);
                if (ObjectUtils.isEmpty(item)) {
                    dto.setItemName(MemberConstant.DEFAULT_ITEM_NAME);
                } else {
                    dto.setItemName(item.getItemName());
                }
                if (ObjectUtils.isEmpty(dto.getMemberId())) {
                    dto.setMemberName(MemberConstant.DEFAULT_MEMBER_NAME);
                    dto.setMemberNo(MemberConstant.DEFAULT_MEMBER_NO);
                } else {
                    MemberInfoDTO member = memberMap.getOrDefault(dto.getMemberId(), null);
                    if (ObjectUtils.isEmpty(member)) {
                        dto.setMemberName(MemberConstant.DEFAULT_MEMBER_NAME);
                        dto.setMemberNo(MemberConstant.DEFAULT_MEMBER_NO);
                    } else {
                        dto.setMemberName(member.getMemberName());
                        dto.setMemberNo(member.getMemberNo());
                    }
                }
            }
        }
    }

    /**
     * 根据会员代金券ID集合查询会员代金券信息
     *
     * @param vouMemIds 会员代金券ID集合
     * @return 查询结果集合
     */
    private Map<String, VoucherMemberDTO> getVouMemMap(List<String> vouMemIds) {
        Map<String, VoucherMemberDTO> dataMap = Maps.newHashMap();
        if (!CollectionUtils.isEmpty(vouMemIds)) {
            QueryVouMemForm form = new QueryVouMemForm();
            form.setIds(Lists.newArrayList(vouMemIds));
            List<VoucherMemberDTO> dataList = voucherMemberService.getList(form);
            if (!CollectionUtils.isEmpty(dataList)) {
                dataList.forEach(dto -> dataMap.put(dto.getId(), dto));
            }
        }
        return dataMap;
    }

    /**
     * 组装查询参数
     *
     * @param form 查询参数
     * @return 查询对象
     */
    @SuppressWarnings("DuplicatedCode")
    private LambdaQueryWrapper<ExpenseBill> getQueryParams(QueryExpenseForm form) {
        LambdaQueryWrapper<ExpenseBill> qw = new QueryWrapper<ExpenseBill>().lambda().eq(ExpenseBill::getDeleted, Boolean.FALSE);
        MyShopDTO shop = shopService.getMyShop();
        qw.eq(ExpenseBill::getShopId, shop.getId());
        if (!ObjectUtils.isEmpty(form.getNameOrNo())) {
            QueryMemberForm memberForm = new QueryMemberForm();
            memberForm.setNameOrNo(form.getNameOrNo());
            List<MemberInfoDTO> memberList = memberService.getMemberList(memberForm);
            List<String> memberIds = Lists.newArrayList();
            if (!CollectionUtils.isEmpty(memberList)) {
                for (MemberInfoDTO dto : memberList) {
                    memberIds.add(dto.getId());
                }
            }
            if (CollectionUtils.isEmpty(memberIds)) {
                memberIds.add(UUID.randomUUID().toString());
            }
            qw.in(ExpenseBill::getMemberId, memberIds);
        }
        if (!ObjectUtils.isEmpty(form.getQueryTimeStart())) {
            qw.ge(ExpenseBill::getBillTime, form.getQueryTimeStart());
        }
        if (!ObjectUtils.isEmpty(form.getQueryTimeEnd())) {
            qw.le(ExpenseBill::getBillTime, form.getQueryTimeEnd());
        }
        if (!ObjectUtils.isEmpty(form.getMemberId())) {
            qw.eq(ExpenseBill::getMemberId, form.getMemberId());
        }
        return qw;
    }

    /**
     * 处理折扣信息
     *
     * @param form   消费参数
     * @param entity 消费账单对象
     */
    private void handleDiscount(ExpenseAddForm form, ExpenseBill entity) {
        entity.setOriginalPrice(form.getAmount());
        if (ObjectUtils.isEmpty(form.getDiscount())) {
            entity.setDiscount(MemberConstant.DEFAULT_DISCOUNT);
        } else {
            entity.setDiscount(form.getDiscount());
        }
        if (ObjectUtils.isEmpty(form.getMemberId())) {
            //非会员
            entity.setFinalPrice(form.getAmount());
        } else {
            //会员
            UpdateBalanceForm balanceForm = new UpdateBalanceForm();
            balanceForm.setMemberId(form.getMemberId());
            balanceForm.setAmount(form.getAmount());
            balanceForm.setGiftPrice(form.getGiftPrice());
            BigDecimal finalAmount = memberService.updateBalance(balanceForm, BalanceOperateEnum.EXPENSE);
            entity.setFinalPrice(finalAmount);
        }
    }

    /**
     * 检查代金券信息
     *
     * @param getVouMemId 会员代金券ID
     */
    private void checkVoucher(String getVouMemId) {
        if (!ObjectUtils.isEmpty(getVouMemId)) {
            VoucherMemberDTO voucher = voucherMemberService.getInfoById(getVouMemId);
            VoucherStatusEnum status = VoucherStatusEnum.get(voucher.getVoucherStatus());
            if (status != VoucherStatusEnum.IN_EFFECT && status != VoucherStatusEnum.UN_USE) {
                throw new BusinessException(MyResponseCode.VOUCHER_STATUS_ERROR);
            }
        }
    }

    /**
     * 更新代金券状态
     *
     * @param getVouMemId 代金券ID
     */
    private void updateVoucherStatus(String getVouMemId) {
        if (!ObjectUtils.isEmpty(getVouMemId)) {
            Boolean flag = voucherMemberService.updateVoucherStatus(getVouMemId, VoucherStatusEnum.USE);
            log.info("代金券信息处理完成, flag = {}", flag);
        }
    }
}
